home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’96 / Roaster-Java-WA-HTTP-CGI hack / cgi WA plugin / cgi.cp next >
Encoding:
Text File  |  1996-06-22  |  17.5 KB  |  633 lines  |  [TEXT/MPS ]

  1. // Sample field protection plugin, using the industry standart ROT13 algorithm.
  2.  
  3.  
  4. #include "ArrangeCallbacks.h"
  5. #include "PluginLibrary.h"
  6.  
  7. #include <Dialogs.h>
  8. #include <StandardFile.h>
  9. #include <string.h>
  10. #include <Folders.h>
  11. #include <TextUtils.h>
  12. #include <Resources.h>
  13. #include <AppleEvents.h>
  14. #include <PLStringFuncs.h>        /* some special string handling stuff */
  15.  
  16. #include <stdio.h>
  17.  
  18. #if defined (__MWERKS__) && defined (PLUGIN_GLOBALS)
  19.     #include "A4Stuff.h"
  20. #endif
  21.  
  22. #define ModuleRsrcID -0x8000
  23.  
  24. #define OurModuleID    0x72310000        //Unique module ID assigned by Phil Meyer
  25. //#define OurModuleID  0x70001513  // Unique module ID; replace with a value
  26.                                            // obtained from Common Knowledge.
  27. #define baseCmdCode  OurModuleID
  28.  
  29. #define pluginName    "CGI" // controls our name in the About menu
  30. #define aboutCmdCode (baseCmdCode + 0)
  31. #define reformatText (baseCmdCode + 1)
  32.  
  33. #if defined (__MWERKS__) && defined (PLUGIN_GLOBALS)
  34.     #define SetupA4            long    lOldA4 = SetCurrentA4 ();
  35.     #define TearDownA4         SetA4 (lOldA4);
  36.     #define UnloadSegs        UnloadSegsFunc ();
  37.  
  38.     static void UnloadSegsFunc ();
  39. #else
  40.     #define SetupA4
  41.     #define TearDownA4
  42.     #define UnloadSegs
  43. #endif    //__MWERKS__
  44.  
  45. typedef struct {
  46.         const ArrangeCallbackTbl* calls;
  47.     } myprefs;
  48.  
  49. class Plugin
  50.     {
  51. public:
  52.     Plugin(const ArrangeCallbackTbl* theCalls);
  53.     ~Plugin();
  54.     
  55.     arHookResult ClickNotify( arClickLocation loc, Point where, Short modifiers,
  56.                                       Short clickCount, arNoteID note, arFieldID field,
  57.                                       arPathID path );
  58.     arHookResult KeyNotify  ( Short theChar, Short key, Short modifiers );
  59.     arHookResult MenuNotify ( Integer commandCode, Integer commandParam,
  60.                                       Short modifiers );
  61.     void AboutToMenu();
  62.     arHookResult FieldNotify( arNoteID note, arFieldID field, arFieldAction action,
  63.                                       const char* choiceText );
  64.     void         TopicNotify( arTopicID newTopic, arWindowID newWindow,
  65.                                       arTopicAction action );
  66.     void             TickNotify (  );
  67.     arHookResult FileNotify ( arFileAction action );
  68.     arHookResult QuitNotify (  );
  69.     void         ATMNotify  (  );
  70.     void             FormatTheNotes (  );
  71.     
  72. private:
  73.     const ArrangeCallbackTbl* calls; // callback table
  74.     myprefs    theprefs;
  75.     }; // Plugin
  76.  
  77.  
  78. /*************************************************************************/
  79. /**************************** Main entry point ***************************/
  80. /*************************************************************************/
  81.  
  82. /* Root entry point for the module - must be the first function in the
  83.  * file, so that the linker will place it first in the code segment.
  84.  * This is the routine which Arrange calls at application startup time
  85.  * (and again at quit time).
  86.  * 
  87.  * Most plug-ins will not need to modify this routine or the Hook functions
  88.  * immediately following.  All customization can be done in the "Plugin"
  89.  * section (below).
  90.  */
  91. extern "C" Integer main( ModuleParamBlock *pb, pModuleRootAction action,
  92.                                             Integer /*p1*/ )
  93.     {
  94.     SetupA4;
  95.  
  96.     Integer    lResult = 0;
  97.  
  98.     /* Extract the callback table from our parameter block, and coerce it
  99.      * to the extended Arrange table.
  100.      */
  101.     ArrangeCallbackTbl* calls = (ArrangeCallbackTbl*)(pb->calls);
  102.     
  103.     Assert (sizeof (Byte) == 1);
  104.     Assert (sizeof (Short) == 2);
  105.     Assert (sizeof (Integer) == 4);
  106.     Assert (sizeof (Float) == 8);
  107.  
  108.     switch (action)
  109.         {
  110.         case mrLoad:
  111.             {
  112.             // Allocate memory and create a new Plugin object.
  113.             void* storage = calls->mem->AllocMem( sizeof(Plugin),
  114.                                                               amFreeStore | amErrIfNoMem );
  115.             pb->moduleRefcon = uInteger(new(storage) Plugin(calls));
  116.             lResult = 1;
  117.             }
  118.             break;
  119.         
  120.         case mrUnload:
  121.             {
  122.             /* Dispose of the Plugin object and release the memory
  123.              * it occupies.
  124.              */
  125.             delete (Plugin*)(pb->moduleRefcon);
  126.             calls->mem->DeallocMem((void*)(pb->moduleRefcon), amFreeStore);
  127.             lResult = 0;
  128.             }
  129.             break;
  130.         
  131.         case mrFindEntry:
  132.         default:
  133.             {
  134.             /* This module contains no extended entry points, so we always
  135.              * return 0 for the mrFindEntry message.  Most plug-ins will not
  136.              * use extended entry points.
  137.              */
  138.             lResult = 0;
  139.             }
  140.             break;
  141.         
  142.         } // switch (action)
  143.     
  144.     TearDownA4;
  145.     return lResult;
  146.     } // main
  147.  
  148. #if defined (__MWERKS__) && defined (PLUGIN_GLOBALS)
  149. #pragma segment PluginLib
  150. static void PluginLibSeg (void)
  151. {
  152. }
  153.  
  154. #pragma segment Main
  155. static void UnloadSegsFunc ()
  156. {
  157.     // Put segment unloading code here.
  158.     // Do NOT use UnloadSeg!
  159.     //UnloadA4Seg (PluginLibSeg);
  160. }
  161. #endif    //__MWERKS__ && PLUGIN_GLOBALS
  162.  
  163. /* These hook functions simply pass control to the appropriate function
  164.  * in the Plugin object.
  165.  */
  166. static arHookResult OurClickHook( ModuleParamBlock* pb, arClickLocation loc,
  167.                                              Point where, pShort modifiers, pShort clickCount,
  168.                                              arNoteID note, arFieldID field,
  169.                                              arPathID path ENDP )
  170.     {
  171.     SetupA4;
  172.     arHookResult    eResult = ((Plugin*)(pb->moduleRefcon))->ClickNotify( loc, where, modifiers,
  173.                                                                         clickCount, note, field,
  174.                                                                         path );
  175.     TearDownA4;
  176.     return eResult;
  177.     }
  178.  
  179.  
  180. static arHookResult OurKeyHook( ModuleParamBlock* pb, pShort theChar, pShort key,
  181.                                           pShort modifiers ENDP )
  182.     {
  183.     SetupA4;
  184.     arHookResult    eResult = ((Plugin*)(pb->moduleRefcon))->KeyNotify(theChar, key, modifiers);
  185.     TearDownA4;
  186.     return eResult;
  187.     }
  188.  
  189.  
  190. static arHookResult OurMenuHook( ModuleParamBlock* pb, Integer commandCode,
  191.                                             Integer commandParam, pShort modifiers ENDP )
  192.     {
  193.     SetupA4;
  194.     arHookResult    eResult = ((Plugin*)(pb->moduleRefcon))->MenuNotify( commandCode, commandParam,
  195.                                                                      modifiers );
  196.     TearDownA4;
  197.     return eResult;
  198.     }
  199.  
  200.  
  201. void OurATMHook(ModuleParamBlock* pb)
  202.     {
  203.     ((Plugin*)(pb->moduleRefcon))->AboutToMenu();
  204.     } // OurATMHook
  205.  
  206.  
  207. static arHookResult OurFieldHook( ModuleParamBlock* pb, arNoteID note,
  208.                                              arFieldID field, arFieldAction action,
  209.                                              const char* choiceText ENDP )
  210.     {
  211.     SetupA4;
  212.     arHookResult    eResult = ((Plugin*)(pb->moduleRefcon))->FieldNotify( note, field, action,
  213.                                                                         choiceText );
  214.     TearDownA4;
  215.     return eResult;
  216.     }
  217.  
  218.  
  219. static void OurTopicHook( ModuleParamBlock* pb, arTopicID newTopic,
  220.                                   arWindowID newWindow, arTopicAction action ENDP )
  221.     {
  222.     SetupA4;
  223.     ((Plugin*)(pb->moduleRefcon))->TopicNotify(newTopic, newWindow, action);
  224.     TearDownA4;
  225.     }
  226.  
  227.  
  228. static void OurTickHook(ModuleParamBlock* pb ENDP)
  229.     {
  230.     SetupA4;
  231.     ((Plugin*)(pb->moduleRefcon))->TickNotify();
  232.     TearDownA4;
  233.     }
  234.  
  235.  
  236. static arHookResult OurFileHook(ModuleParamBlock* pb, arFileAction action ENDP)
  237.     {
  238.     SetupA4;
  239.     arHookResult    eResult = ((Plugin*)(pb->moduleRefcon))->FileNotify(action);
  240.     TearDownA4;
  241.     return eResult;
  242.     }
  243.  
  244.  
  245. static arHookResult OurQuitHook(ModuleParamBlock* pb ENDP)
  246.     {
  247.     SetupA4;
  248.     arHookResult    eResult = ((Plugin*)(pb->moduleRefcon))->QuitNotify();
  249.     TearDownA4;
  250.     return eResult;
  251.     }
  252.  
  253. /*************************************************************************/
  254. /********************************* Plugin ********************************/
  255. /*************************************************************************/
  256.  
  257. /* Construct a Plugin object.  This is called once, from the OurModuleRoot
  258.  * function, when the module is initialized (i.e. at application startup time).
  259.  */
  260. Plugin::Plugin(const ArrangeCallbackTbl* theCalls)
  261.     {
  262.     short    thelength;
  263.     arDocumentPtr    theCurDoc;
  264.     arDocumentPtr    thePrefDoc;
  265.     Str255            ourname;
  266.     Handle            temph;
  267.     
  268.     // Record the callback table for future use.
  269.     calls = theCalls;
  270.     
  271.     /* These commands, if un-commented-out, register this plugin to be called
  272.      * by Arrange when various events occur.  For example, if you un-comment-out
  273.      * the call to SetClickHook, then Plugin::ClickNotify will be called
  274.      * whenever the user clicks in any of the locations described in the
  275.      * arClickLocation enum.
  276.      */
  277.     // calls->ui->SetClickHook(OurClickHook, 0, true);
  278.     // calls->ui->SetKeyHook  (OurKeyHook,   0, true, charFilter, keyFilter, modFilter);
  279.     // calls->ui->SetMenuHook (OurMenuHook,  0, true, whichCommand);
  280.     // calls->ui->SetFieldHook(OurFieldHook, 0, true, whichField);
  281.     //calls->ui->SetTopicHook(OurTopicHook, 0, true);
  282.     // calls->ui->SetFileHook (OurFileHook,  0, true);
  283.     //calls->ui->SetQuitHook (OurQuitHook,  0, true);
  284.     calls->ui->SetTickHook (OurTickHook,  0, true);
  285.     calls->ui->SetATMHook  (OurATMHook,   0, true);
  286.     
  287.     // Add an item to the About Plugins menu for this plugin.
  288.     temph = GetResource('STR ', ModuleRsrcID+1); // "About Protect"
  289.     BlockMove ((Ptr)*temph, (Ptr)(&ourname), 50);
  290.     ourname[ourname[0]+1] = 0; //Make good for C String
  291.     calls->ui->AddMenuItem(mPluginAbout,  (char *)(&ourname[1]), 0, aboutCmdCode, 1);
  292.     
  293.     temph = GetResource('STR ', ModuleRsrcID+2); // "Protect Text"
  294.     BlockMove ((Ptr)*temph, (Ptr)(&ourname), 50);
  295.     ourname[ourname[0]+1] = 0; //Make good for C String
  296.     calls->ui->AddMenuItem(mEdit, (char *)(&ourname[1]),     0, reformatText,    2);    
  297.     
  298.     calls->ui->SetMenuHook(OurMenuHook, 1, true, aboutCmdCode);
  299.     calls->ui->SetMenuHook(OurMenuHook, 2, true, reformatText);
  300.     
  301.     } // Plugin constructor
  302.  
  303.  
  304. /* Dispose of a Plugin object.  This is called when Arrange terminates.  You
  305.  * should free up any data structures which you allocation in the Plugin
  306.  * constructor (above).
  307.  */
  308. Plugin::~Plugin()
  309.     {
  310.     } // ~Plugin
  311.  
  312.  
  313. /* This function is called whenever the user clicks in an "interesting place"
  314.  * in a document window.  It should return true if we handle the click, false
  315.  * (the normal case) when the click should be passed along to other plug-ins
  316.  * or to Arrange's normal event processing.  See the documentation for
  317.  * SetClickHook for more details.
  318.  */
  319. arHookResult Plugin::ClickNotify( arClickLocation loc, Point where,
  320.                                              Short modifiers, Short clickCount,
  321.                                              arNoteID note, arFieldID field,
  322.                                              arPathID path )
  323.     {
  324.     return false; // Let Arrange handle the event
  325.     } // ClickNotify
  326.  
  327.  
  328. /* This function is called whenever the user types a key which matches the
  329.  * filters we pass to SetKeyHook in Plugin's constructor.  It should return
  330.  * true if we handle the event, false (the normal case) when the event
  331.  * should be passed along to other plug-ins or to Arrange's normal event
  332.  * processing.  See the documentation for SetKeyHook for more details.
  333.  */
  334. arHookResult Plugin::KeyNotify(Short theChar, Short key, Short modifiers)
  335.     {
  336.     return false; // Let Arrange handle the event
  337.     } // KeyNotify
  338.  
  339. // OK, we get the notes, get their text, and do cool stuff with them!
  340.  
  341. static Boolean ProtectText(const ArrangeCallbackTbl* calls, Ptr theptr, short* thelength)
  342. {
  343.     
  344.     short foundVRefNum, refNum;
  345.     long    foundDirID, count;
  346.     
  347.     OSErr iErr;
  348.     
  349.     FSSpec fs;
  350.     
  351.     count = *thelength;
  352.     
  353.     iErr = FindFolder(kOnSystemDisk, 'trsh', 0, &foundVRefNum, &foundDirID);
  354.     iErr = FSMakeFSSpec(foundVRefNum, 0, "\pcgi.txt", &fs);
  355.     
  356.     iErr = FSpDelete(&fs);
  357.     
  358.     iErr = FSpCreate(&fs, 'Rstr', 'TEXT', smSystemScript);
  359.     iErr = FSpOpenDF(&fs, fsWrPerm, &refNum);
  360.     iErr = FSWrite(refNum, &count, theptr);
  361.     iErr = FSClose(refNum);
  362.     
  363.     return 1;
  364. }
  365.  
  366. void    Plugin::FormatTheNotes (  )
  367. {
  368.     Integer        i;    
  369.     short    whichnote;
  370.     // Determine whether there is a selection and how many notes it contains.
  371.     Integer selCount;
  372.     
  373.     arNoteID  selNote;
  374.     arNoteID     parentNote;
  375.     arFieldID selField;
  376.     Integer   selStart;
  377.     Integer   selEnd;
  378.     
  379.     short        thecount;
  380.     arFieldID    theField;
  381.     arFieldInfo info;
  382.     arNoteInfo    thetopicinfo;
  383.     short    thelength;
  384.     Str255    tempstr;
  385.     Ptr        theptr;
  386.     char        TheName[32]="Protecting";
  387.     Boolean    firstchange = true;
  388.     
  389.     Str31 promptstr = "Now protecting";
  390.     Handle    temph;
  391.     
  392.     temph = GetResource('STR ', ModuleRsrcID+3); // "Now Reformatting Text"
  393.     BlockMove((Ptr)*temph, &promptstr, 32);
  394.     
  395.     promptstr[promptstr[0]+1] = 0;
  396.     
  397.     calls->dlg->DisplayNotify( (const char*)&promptstr[1], nfShowImmediately);
  398.     
  399.     if (!calls->sel->FlushSelection(false))
  400.     {
  401.         calls->dlg->ClearNotify();
  402.         return;
  403.     }
  404.  
  405.     switch (calls->sel->GetSelection(&selNote, &selField, &selStart, &selEnd))
  406.         {
  407.         case stNote:
  408.         case stFieldContents:
  409.             selCount = 1;
  410.             break;
  411.         
  412.         case stMultipleNotes:
  413.         case stMultipleFields:
  414.             selCount = selStart;
  415.             break;
  416.         
  417.         case stField:
  418.                 thelength = calls->data->GetFieldTextLen(selNote, selField);
  419.                 if (thelength)
  420.                 {
  421.                     info.versNum = 1;
  422.                     calls->sysObj->GetFieldInfo(selField, &info);
  423.                     thelength = calls->data->GetFieldTextLen(selNote, selField);
  424.                     if (thelength)
  425.                     {
  426.                         theptr = NewPtr(thelength+1);
  427.                         if (!theptr)
  428.                         {
  429.                             calls->dlg->ClearNotify();
  430.                             return;
  431.                         }
  432.                         thelength = calls->data->GetFieldText(selNote, selField, thelength+1,  (char*)theptr);
  433.                         if (ProtectText(calls, theptr, &thelength))
  434.                         {
  435. #if 0
  436.                             if (firstchange)
  437.                             {
  438.                                 calls->doc->SetupUndo(TheName, true);
  439.                                 firstchange = false;
  440.                             }
  441.  
  442.                             calls->data->SetFieldText(selNote, selField, (char*)theptr, thelength, 0);
  443. #endif
  444.                             calls->sel->FlushSelection(false);
  445.                         }
  446.                         DisposePtr(theptr);
  447.                     }
  448.                 }
  449.                 calls->dlg->ClearNotify();
  450.                 return;
  451.                 break;
  452.         case stText:  // selected text is treated specially
  453.                 thelength = calls->data->GetFieldTextLen(selNote, selField);
  454.                 if (thelength)
  455.                 {
  456.                     theptr = NewPtr(thelength+1);
  457.                     if (!theptr)
  458.                     {
  459.                         calls->dlg->ClearNotify();
  460.                         return;
  461.                     }
  462.                     thelength = calls->sel->GetSelText(thelength+1,  (char*)theptr, &selStart, &selEnd);
  463.                     thelength = selEnd-selStart;
  464.                     *((Ptr)(theptr+selEnd+1)) = 0;
  465.                     if (ProtectText(calls, (Ptr)(theptr+selStart), &thelength))
  466.                     {
  467.                         //calls->sel->ReplaceSelText((Ptr)(theptr+selStart), TheName);
  468.                         calls->sel->FlushSelection(false);
  469.                     }
  470.                     DisposePtr(theptr);
  471.                 }
  472.                 calls->dlg->ClearNotify();
  473.                 return;
  474.                 break;
  475.         default:
  476.             calls->dlg->ClearNotify();
  477.             return;
  478.         
  479.         } // switch (GetSelection result)
  480.     
  481.     // Iterate over each selected note.
  482.     for (whichnote=0; whichnote<selCount; whichnote++)
  483.         {
  484.             calls->sel->GetSelEntry(whichnote, &selNote, &selField, &parentNote);
  485.             if (selNote != nil)
  486.             {
  487.                 calls->notes->GetNoteInfo(selNote, &thetopicinfo);
  488.                 thecount = calls->notes->CountNoteFields(selNote);
  489.                 for (i=0; i<thecount; i++)
  490.                 {
  491.                     theField = calls->notes->GetNoteField(selNote, i);
  492.                     if (theField!=nil)
  493.                     {
  494.                         info.versNum = 1;
  495.                         calls->sysObj->GetFieldInfo(theField, &info);
  496.                         if (info.type==arFTText)
  497.                         {
  498.                             thelength = calls->data->GetFieldTextLen(selNote, theField);
  499.                             if (thelength)
  500.                             {
  501.                                 theptr = NewPtr(thelength+1);
  502.                                 if (!theptr)
  503.                                 {
  504.                                     calls->dlg->ClearNotify();
  505.                                     return;
  506.                                 }
  507.                                 thelength = calls->data->GetFieldText(selNote, theField, thelength+1,  (char*)theptr);
  508.                                 if (ProtectText(calls, theptr, &thelength))
  509.                                 {
  510.                                 /*
  511.                                     if (firstchange)
  512.                                     {
  513.                                         calls->doc->SetupUndo(TheName, true);
  514.                                         firstchange = false;
  515.                                     }
  516.                                 */
  517.                                     //calls->data->SetFieldText(selNote, theField, (char*)theptr, thelength, 0);
  518.                                     calls->sel->FlushSelection(false);
  519.                                 }
  520.                                 DisposePtr(theptr);
  521.                             }
  522.                         }
  523.                     }
  524.                 }
  525.             }
  526.  
  527.         } // whichnote loop
  528.     calls->dlg->ClearNotify();
  529.     return;
  530. }
  531.  
  532. /* This function is called whenever the user clicks in the menu bar or types
  533.  * a command key, just before processing the event.  It does any fixing up
  534.  * of menus which might be necessary based on the current state of affairs.
  535.  */
  536. void Plugin::AboutToMenu()
  537. {
  538.     arNoteID selNote;
  539.     arFieldID selField;
  540.     Integer selStart,selEnd;
  541.     arSelType selType = calls->sel->GetSelection( &selNote, &selField,
  542.                                                                 &selStart, &selEnd );
  543.     
  544.     Boolean hasSingleSel = ( selType == stNote || selType == stMultipleNotes || selType == stText || selType == stField );
  545.     
  546.     calls->ui->SetMenuItem(mEdit, reformatText, 2, nil, ((selType == stNote) || (selType == stMultipleNotes) || (selType == stText) || (selType == stField)), 0, 0);
  547.  
  548. }
  549.  
  550. arHookResult Plugin::MenuNotify( Integer commandCode, Integer commandParam,
  551.                                             Short modifiers )
  552.     {
  553.     ProcessSerialNumber thepsn;
  554.     arTopicID    thistopic;
  555.     arTopicInfo    topicinfo;
  556.     
  557.     /* If this is our About menu item, display our about-box dialog and return
  558.      * true to indicate we handled the command.
  559.      */
  560.     if (commandCode == aboutCmdCode)
  561.         {
  562.         Alert(ModuleRsrcID, nil);
  563.         return true;
  564.         }
  565.     if (commandCode == reformatText)
  566.         {
  567.         FormatTheNotes();
  568.         return true;
  569.         }
  570.     return false; // Let Arrange handle the event
  571.     
  572.     } // MenuNotify
  573.  
  574.  
  575. /* If we register to recieve events for a field by calling SetFieldHook, this
  576.  * function will be called for any events in that field.  It should return
  577.  * false in almost all cases.  See the documentation for SetFieldHook for
  578.  * more details.
  579.  */
  580. arHookResult Plugin::FieldNotify( arNoteID note, arFieldID field,
  581.                                              arFieldAction action, const char* choiceText )
  582.     {
  583.     return false;
  584.     } // FieldNotify
  585.  
  586.  
  587. /* This function is called whenever the user switches windows or changes
  588.  * the current folder/topic/view in the front window.  See the documentation
  589.  * for SetTopicHook for more details.
  590.  */
  591. void Plugin::TopicNotify( arTopicID newTopic, arWindowID newWindow,
  592.                                   arTopicAction action )
  593.     {
  594.     } // TopicNotify
  595.  
  596.  
  597. /* This function is called periodically, whenever Arrange recieves a null
  598.  * event from the Event Manager.
  599.  */
  600. void Plugin::TickNotify()
  601. {
  602. } // TickNotify
  603.  
  604.  
  605. /* This function is called whenever various file-level events occur.  It
  606.  * should return true in almost all cases.  See the documentation for
  607.  * SetFileHook for more details.
  608.  */
  609. arHookResult Plugin::FileNotify(arFileAction action)
  610.     {
  611.     return true;
  612.     } // FileNotify
  613.  
  614.  
  615. /* This function is called if the user voluntarily quits Arrange.  It should
  616.  * return true to allow the user to quit, false to prevent it.  See the
  617.  * documentation for SetQuitHook for more details.
  618.  */
  619. arHookResult Plugin::QuitNotify()
  620.     {
  621.     return true;
  622.     } // QuitNotify
  623.  
  624.  
  625. /* This function is called whenever the user clicks in the menu bar or types
  626.  * a command key, just before processing the event.  It should do any fixing
  627.  * up of menus which might be necessary based on the current state of affairs.
  628.  * See the documentation for SetATMHook for more details.
  629.  */
  630. void Plugin::ATMNotify()
  631.     {
  632.     } // ATMNotify
  633.